/// <reference path="../typings/jquery/jquery.d.ts" />
/// <reference path="../typings/knockout/knockout.d.ts" />
/// <reference path="../disco/disco.model.ts" />

module disco.model.helper {

    declare var OData;

    export class DataContextManager {
        private dataContext: DataContext;

        constructor(dataContext: DataContext) {
            this.dataContext = dataContext;
        }

        public getDataContext(): DataContext {
            return this.dataContext;
        }

        public setDataContext(context: DataContext) {
            this.dataContext = context;
        }

        public getMetadata(): any {
            return this.getDataContext().metadata;
        }

        //public getEntityTypeCollection(): EntityType[] {
        //    return this.getDataContext().entityTypes;
        //}

        //public getEntitySetCollection(): EntitySet[] {
        //    return this.getDataContext().entitySets;
        //}

        public generateEntityModel() {
            this.generateEntityTypes();
            this.generateEntitySets();
        }

        public generateEntitySets() {
            var context: DataContext = this.getDataContext();
            var metaEntitySets: any[] = this.getEntitySetsMetadata();

            metaEntitySets.forEach(metaEntitySet => {
                var setTypeName: string = Namespace.remove(metaEntitySet.entityType);

                var set: EntitySet = new EntitySet();
                set.name = metaEntitySet.name;
                set.type = this.getDataContext().entityType(setTypeName);
                set.type.entitySet = set;

                this.getDataContext().entitySet(set.type.name, set);
            });
        }

        public generateEntityTypes() {
            this.generateBasicEntityTypes();
            this.applyBaseTypes();
            this.addNavigationToEntityProperties();
        }

        private generateBasicEntityTypes() {
            var associations = this.getAssociationsMetadata();
            var typeMgr: EntityTypeManager = new EntityTypeManager();

            this.getEntityTypesMetadata().forEach((metaEntityType) => {
                typeMgr.setEntityType();
                typeMgr.generateSimpleProperties(metaEntityType, associations);

                var type = typeMgr.getEntityType();
                this.getDataContext().entityType(type.name, type);
            });
        }

        private applyBaseTypes() {
            var typeMgr: EntityTypeManager = new EntityTypeManager();

            Object.keys(this.getDataContext().entityTypes).forEach(key => {
                typeMgr.setEntityType(this.getDataContext().entityTypes[key]);
                typeMgr.applyBaseType(this.getDataContext());
            });
        }

        private addNavigationToEntityProperties() {
            var typeMgr: EntityTypeManager = new EntityTypeManager();

            Object.keys(this.getDataContext().entityTypes).forEach(key => {
                typeMgr.setEntityType(this.getDataContext().entityTypes[key]);
                typeMgr.addNavigationToEntityProperties(this.getDataContext());
            });
        }

        private getEntityTypesMetadata(): any[] {
            return this.getMetadataServices().schema[0].entityType;
        }
        private getAssociationsMetadata(): any[] {
            return this.getMetadataServices().schema[0].association;
        }
        private getEntitySetsMetadata(): any[] {
            var dataServices: DataServices;
            var schemaIndex: number = (this.getMetadataServices().schema.length > 1) ? 1 : 0; // hack
            return this.getMetadataServices().schema[schemaIndex].entityContainer[0].entitySet;
        }

        private getMetadataServices(): DataServices {
            return this.getMetadata().dataServices;
        }
    }
    export class EntitySetManager extends core.DataManager<EntitySet> {
        public add(entity: Entity) {
            if (!Collections.contains(this.get().rows(), e => e.property('Id').value == entity.property('Id').value))
                this.get().rows.push(entity);
        }
    }
    export class EntityManager extends core.DataManager<Entity> {
        private context: DataContext;

        constructor(context: DataContext) {
            super();
            this.context = context;
        }

        //creates Entity from raw disco.web request data
        public fromRawEntity(rawEntity): Entity {
            var entity = this.set(new Entity());

            var typeString = Namespace.remove(rawEntity['odata.type']);
            entity.type = this.context.entityType(typeString);
            entity.type.properties.forEach(property => {
                var propertyValue = new model.PropertyValue();
                propertyValue.property = property;
                if (property.isNavigation) {
                    //propertyValue.loaded = false;
                    propertyValue.url = rawEntity[property.name + '@odata.navigationLinkUrl'];
                }
                else {
                    //propertyValue.loaded = true;
                    propertyValue.value = rawEntity[property.name];
                }
                entity.property(property.name, propertyValue);
            });

            return entity;
        }
    }
}